Ideas and exercises come from https://r4ds.had.co.nz/transform.html

Additional notes by TCS

Setup

First, we load the tidyverse package and a dataset. This data frame contains all 336,776 flights that departed from New York City in 2013.

The Lahman baseball dataset is also used.

require(nycflights13)
Loading required package: nycflights13
require(tidyverse)
Loading required package: tidyverse
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.3.6      ✔ purrr   0.3.4 
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.2.1      ✔ stringr 1.4.1 
✔ readr   2.1.2      ✔ forcats 0.5.2 ── Conflicts ───────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
require(hms)
Loading required package: hms
require(Lahman)
Loading required package: Lahman
flights
NA

Review: Basic dplyr functions

There are 5 key functions (“verbs”), plus a helper function, that do most data manipulation tasks in dplyr:

filter() gives you a subset of rows based on values

NA handling in filter

filter() includes ONLY rows where the condition is TRUE; it excludes both FALSE and NA values. If you want to preserve missing values, ask for them explicitly:

df <- tibble(x = c(1, NA, 3))
(biggerthan1 <- filter(df, x > 1))
(bigorNA <- filter(df, is.na(x) | x > 1))

arrange() is for sorting rows

select() gives you a subset of columns by name

You can name each column, or specify a range using a colon.
As with other R selections, you can omit certain columns using the minus sign.
You can add multiple arguments to include more columns in the selection.

Partial names

select() does not have to use exact column matches.

  • starts_with("foo")
  • ends_with("bar")
  • contains("foobar")
  • matches(some_regex)
  • num_range("x", 1:3) matches x1, x2 and x3

Variants on select()

  • rename() is considered a variant of select() where you take a column, change its name, and keep all other columns as well.
  • everything() is a helper for select() that lets you move one or a few columns to the beginning (left) of the table, while retaining all other columns.

mutate() is for creating new variables from old

(flights_sml <- select(flights, 
  year:day, 
  ends_with("delay"), 
  distance, 
  air_time
))
(flights_less_sml <- mutate(flights_sml,
  gain = dep_delay - arr_delay,
  speed = distance / air_time * 60
))
(flights_even_more <- mutate(flights_sml,
  gain = dep_delay - arr_delay,
  hours = air_time / 60,
  gain_per_hour = gain / hours
))
(flights_new <- transmute(flights,
  gain = dep_delay - arr_delay,
  hours = air_time / 60,
  gain_per_hour = gain / hours
))

Many vectorized functions work with mutate()

Some examples:
* Arithmetic, logs (vectors recycle to match length)
* Modulus arithmetic: %/% (integer division) and %% (remainder), where x == y * (x %/% y) + (x %% y)
* Surrounding values: lead(), lag()
* Cumulative values such as cumsum() and cumprod()
* With the Rcpproll package, rolling sums, etc.
* Ranking such as min_rank()

Exercises for mutate()

  1. Currently dep_time and sched_dep_time are convenient to look at, but hard to compute with because they’re not really continuous numbers. Convert them to a more convenient representation of number of minutes since midnight.

  2. Compare air_time with arr_time - dep_time. What do you expect to see? What do you see? What do you need to do to fix it?

*The times are in HHMM format, so straight subtraction does not work. We can convert to time objects, or use modulus arithmetic to convert to minutes and back to time.

  1. Compare dep_time, sched_dep_time, and dep_delay. How would you expect those three numbers to be related?

We expect dep_delay = sched_dep_time - dep_time. However, for > 60 min difference, we have the same time issue, and solution, as in exercise 2.

  1. Find the 10 most delayed flights using a ranking function. How do you want to handle ties? Carefully read the documentation for min_rank().
  • rank() goes smallest to largest, gives decimals (averages) for ties, 1, 2.5, 2.5, 4,… and puts NAs last by default, but can change behavior using na.last. rank(desc()) goes largest to smallest.
  • rank() has different methods of handling ties, producing all-integer output. First; last; random; max (max rank of all ties so there might not be a #1); min (min rank of all tied elements “as used in sports,” so you can “tie for 1st” – (1,1,3,4…)) – this is min_rank(). first, last, random produce all-unique ranks.
  • If you preserve or average ties, you could end up with more or fewer than 10 in the result. Therefore to get exactly 10 we will break ties randomly.
  • If you want to use another column to break ties, then I don’t see a way within rank() functions. You could use arrange() and then row numbers.
  1. What does 1:3 + 1:10 return? Why?

The 1:3 vector is recycled, so you get 4+1, 5+2, etc.

  1. What trigonometric functions does R provide?

The usual functions, plus a few others. Angles are in radians. cospi(x) and kin give the functions of pi times x, only for x = multiples of 0.5

# The time is in HMM or HHMM format. Therefore to get the hours we divide by 100, and for minutes we take the remainder. (Did not deal with the times = 2400!)

(better_time <- mutate(flights, dep_min_since_midnight = (dep_time %/% 100) * 60 + dep_time %% 100, sched_dep_min_since_midnight = (sched_dep_time %/% 100) * 60 + sched_dep_time %% 100))

(compare <- select(flights, air_time, arr_time, dep_time) %>% mutate(spent_time = arr_time-dep_time))

# the times are in HHMM format, so straight subtraction does not work

(realtimes <- mutate(flights, 
                    arr_time_hms = hms(NULL, arr_time %% 100, arr_time %/% 100),
                    dep_time_hms = hms(NULL, dep_time %% 100, dep_time %/% 100),
                    spent_time_hms = difftime(arr_time_hms, dep_time_hms, units = "mins")))
  
# mutate to add the ranking function (alternatively you could probably arrange and then take by row number)

# if you preserve or average ties, you could end up with more or fewer than 10 in the result. Therefore to get exactly 10 we will break ties randomly.

(arr_ranked <- mutate(flights, arr_delay_rank = rank(desc(arr_delay), ties.method = "random")))
(top_10_delay <- arr_ranked %>%  filter(arr_delay_rank <= 10))

summarise() collapses groups into single values

*When using summarise() you usually also want groups created by group_by(). (If you didn’t have a group, you could just call the desired functions on whole columns.)

*Using the pipe we can quickly feed groups into summaries and do other useful tasks efficiently.

*Among tidyverse functions, only ggplot2 doesn’t work as well with pipes: “it was written before the pipe was discovered. Unfortunately, the next iteration of ggplot2, ggvis, which does use the pipe, isn’t quite ready for prime time yet.” But it is used in some examples below.

*Beware – if you have any missing values (NAs) in your data your summary value will also be NA, unless you set na.rm = TRUE.

Other functions for summarising [sic]

  • Count, Sum, Median, etc.
  • Measures of spread: sd(x), IQR(x), mad(x). The interquartile range IQR(x) and median absolute deviation mad(x) are robust equivalents that may be more useful if you have outliers.
  • Summaries of logical subsets such as mean(arr_delay[arr_delay > 0]) = the mean of all those values > 0
  • Rankings: min(x), quantile(x, 0.25) [25th percentile value], max(x)
  • Positions: first(x), nth(x, 2), last(x). These work similarly to x[1], x[2], and x[length(x)] but let you set a default value if that position does not exist

# with pipe
(delays <- flights %>% 
  group_by(dest) %>% 
  summarise(
    count = n(),
    dist = mean(distance, na.rm = TRUE),
    delay = mean(arr_delay, na.rm = TRUE)
  ) %>% 
  filter(count > 20, dest != "HNL"))
# plot delay vs distance
(p <- ggplot(data = delays, mapping = aes(x = dist, y = delay)) +
   geom_point(aes(size = count), alpha = 1/3) +
   geom_smooth(se = FALSE)
)

# remove NAs when computing the summary
(delayed <- flights %>% 
  group_by(year, month, day) %>% 
  summarise(mean = mean(dep_delay, na.rm = TRUE)))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# remove NAs by removing data at the beginning
(not_cancelled <- flights %>% 
  filter(!is.na(dep_delay), !is.na(arr_delay))
)
(true_delayed <- not_cancelled %>% 
  group_by(year, month, day) %>% 
  summarise(mean = mean(dep_delay, na.rm = TRUE)))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# which planes have the greatest delays?
(delays <- not_cancelled %>% 
  group_by(tailnum) %>% 
  summarise(
    delay = mean(arr_delay)
  ))
# histogram of average delay times
(p1 <- ggplot(data = delays, mapping = aes(x = delay)) + 
  geom_freqpoly(binwidth = 10))


# remove NAs and cancelled
(delays <- not_cancelled %>% 
  group_by(tailnum) %>% 
  summarise(
    delay = mean(arr_delay, na.rm = TRUE),
    n = n()
  ))
# scatterplot of average delay vs n (rotated histogram actually, but you can see more points)
# shows that the high averages are almost all based on few data points
(p3 <- ggplot(data = delays, mapping = aes(x = n, y = delay)) + 
  geom_point(alpha = 1/10))


# filter out the points with low n before plotting
(p4 <- delays %>% 
  filter(n > 25) %>% 
  ggplot(mapping = aes(x = n, y = delay)) + 
    geom_point(alpha = 1/10))


# instead of averaging early and late flights together, start with the logical subset of the positive (late) delays
(not_cancelled %>% 
  group_by(year, month, day) %>% 
  summarise(
    avg_delay1 = mean(arr_delay),
    avg_delay2 = mean(arr_delay[arr_delay > 0]) # the average positive delay
  ))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# looking at standard deviation of distance to each destination
(spread_of_origins <- not_cancelled %>% 
  group_by(dest) %>% 
  summarise(distance_sd = sd(distance)) %>% 
  arrange(desc(distance_sd)))

# When do the first and last flights leave each day?
(first_and_last <- not_cancelled %>% 
  group_by(year, month, day) %>% 
  summarise(
    first = min(dep_time),
    last = max(dep_time)
  ))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
(first_last_2 <- not_cancelled %>% 
  group_by(year, month, day) %>% 
  summarise(
    first_dep = first(dep_time), # This works ONLY because the table is already ordered by dep_time!
    last_dep = last(dep_time)
  ))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# another way to find the 1st and last per day
(rankfilt <- not_cancelled %>% 
  group_by(year, month, day) %>% 
  mutate(r = min_rank(desc(dep_time))) %>% 
  filter(r %in% range(r))) # range gives you the min and max values

Here we continue digressing (IMO) on the relationship between variation and sample size, using examples from the Lahman baseball statistics package.

# Convert to a tibble so it prints nicely
(batting <- as_tibble(Lahman::Batting))

# batting average vs at bats per player

# who are the "best" batters? The lucky ones? Note the at-bats for the top of the list
(high_avg <- batters %>% 
  arrange(desc(ba))
)
# visualize avg with the number of at-bats 
(batters <- batting %>% 
  group_by(playerID) %>% 
  summarise(
    ba = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
    ab = sum(AB, na.rm = TRUE)
  ))

(p <- batters %>% 
  filter(ab > 100) %>% 
  ggplot(mapping = aes(x = ab, y = ba)) +
    geom_point() + 
    geom_smooth(se = FALSE))

(valid_arr_time <- sum(!is.na(not_cancelled$arr_time)))
[1] 327346
LS0tCnRpdGxlOiAiTm90ZXMgb24gUiBmb3IgRGF0YSBTY2llbmNlIENoYXB0ZXIgNTogRGF0YSB0cmFuc2Zvcm1hdGlvbiwgUGFydCAyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoqSWRlYXMgYW5kIGV4ZXJjaXNlcyBjb21lIGZyb20gaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90cmFuc2Zvcm0uaHRtbCoKCipBZGRpdGlvbmFsIG5vdGVzIGJ5IFRDUyoKCiMgU2V0dXAKRmlyc3QsIHdlIGxvYWQgdGhlIGB0aWR5dmVyc2VgIHBhY2thZ2UgYW5kIGEgZGF0YXNldC4gVGhpcyBkYXRhIGZyYW1lIGNvbnRhaW5zIGFsbCAzMzYsNzc2IGZsaWdodHMgdGhhdCBkZXBhcnRlZCBmcm9tIE5ldyBZb3JrIENpdHkgaW4gMjAxMy4KClRoZSBMYWhtYW4gYmFzZWJhbGwgZGF0YXNldCBpcyBhbHNvIHVzZWQuCgpgYGB7ciBzZXR1cH0KcmVxdWlyZShueWNmbGlnaHRzMTMpCnJlcXVpcmUodGlkeXZlcnNlKQpyZXF1aXJlKGhtcykKcmVxdWlyZShMYWhtYW4pCmZsaWdodHMKCmBgYAoKIyBSZXZpZXc6IEJhc2ljIGRwbHlyIGZ1bmN0aW9ucwoKVGhlcmUgYXJlIDUga2V5IGZ1bmN0aW9ucyAoInZlcmJzIiksIHBsdXMgYSBoZWxwZXIgZnVuY3Rpb24sIHRoYXQgZG8gbW9zdCBkYXRhIG1hbmlwdWxhdGlvbiB0YXNrcyBpbiBkcGx5cjogIAoKKiBgZmlsdGVyYDogcGljayBvYnNlcnZhdGlvbnMgYnkgdmFsdWVzCiogYGFycmFuZ2VgOiByZW9yZGVyIHJvd3MgIAoqIGBzZWxlY3RgOiBwaWNrIHZhcmlhYmxlcyBieSBuYW1lICAKKiBgbXV0YXRlYDogY3JlYXRlIG5ldyB2YXJpYWJsZXMgZnJvbSBleGlzdGluZyBvbmVzLCB1c2luZyBmdW5jdGlvbnMgIAoqIGBzdW1tYXJpc2VgOiBjb2xsYXBzZSB2YWx1ZXMgaW50byBzaW5nbGUgb25lcyAgCiogYGdyb3VwX2J5YDogY2hhbmdlIHNjb3BlIG9mIGEgdmVyYiBmcm9tIHRoZSB3aG9sZSBkYXRhc2V0IHRvIGluZGl2aWR1YWwgZ3JvdXBzIAoKIyMgYGZpbHRlcigpYCBnaXZlcyB5b3UgYSBzdWJzZXQgb2Ygcm93cyBiYXNlZCBvbiB2YWx1ZXMKCiMjIE5BIGhhbmRsaW5nIGluIGBmaWx0ZXJgCgpgZmlsdGVyKClgIGluY2x1ZGVzIE9OTFkgcm93cyB3aGVyZSB0aGUgY29uZGl0aW9uIGlzIFRSVUU7IGl0IGV4Y2x1ZGVzIGJvdGggRkFMU0UgYW5kIE5BIHZhbHVlcy4gSWYgeW91IHdhbnQgdG8gcHJlc2VydmUgbWlzc2luZyB2YWx1ZXMsIGFzayBmb3IgdGhlbSBleHBsaWNpdGx5OiAgCgpgYGB7ciBmaWx0ZXIgTkEgZXhhbXBsZXMsIGVjaG8gPSBUUlVFfQpkZiA8LSB0aWJibGUoeCA9IGMoMSwgTkEsIDMpKQooYmlnZ2VydGhhbjEgPC0gZmlsdGVyKGRmLCB4ID4gMSkpCihiaWdvck5BIDwtIGZpbHRlcihkZiwgaXMubmEoeCkgfCB4ID4gMSkpCmBgYAojIGBhcnJhbmdlKClgIGlzIGZvciBzb3J0aW5nIHJvd3MKCiogVGhlIGFyZ3VtZW50cyBmb3IgYGFycmFuZ2UoKWAgYXJlIGEgZGF0YWZyYW1lIGFuZCBjb2x1bW4gbmFtZShzKS4gICAKKiBUaGUgYGRlc2NgIG9wdGlvbiByZXZlcnNlcyB0aGUgb3JkZXIuICAKKiBNaXNzaW5nIHZhbHVlcyAoTkFzKSBhcmUgYWx3YXlzIGF0IHRoZSBlbmQgcmVnYXJkbGVzcyBvZiBgZGVzYygpYCAKIAojIGBzZWxlY3QoKWAgZ2l2ZXMgeW91IGEgc3Vic2V0IG9mIGNvbHVtbnMgYnkgbmFtZQoKWW91IGNhbiBuYW1lIGVhY2ggY29sdW1uLCBvciBzcGVjaWZ5IGEgcmFuZ2UgdXNpbmcgYSBjb2xvbi4gIApBcyB3aXRoIG90aGVyIFIgc2VsZWN0aW9ucywgeW91IGNhbiBvbWl0IGNlcnRhaW4gY29sdW1ucyB1c2luZyB0aGUgbWludXMgc2lnbi4gIApZb3UgY2FuIGFkZCBtdWx0aXBsZSBhcmd1bWVudHMgdG8gaW5jbHVkZSBtb3JlIGNvbHVtbnMgaW4gdGhlIHNlbGVjdGlvbi4gIAoKIyMgUGFydGlhbCBuYW1lcyAgCgpgc2VsZWN0KClgIGRvZXMgbm90IGhhdmUgdG8gdXNlIGV4YWN0IGNvbHVtbiBtYXRjaGVzLiAgCgoqIGBzdGFydHNfd2l0aCgiZm9vIilgICAKKiBgZW5kc193aXRoKCJiYXIiKWAgIAoqIGBjb250YWlucygiZm9vYmFyIilgICAKKiBgbWF0Y2hlcyhzb21lX3JlZ2V4KWAgIAoqIGBudW1fcmFuZ2UoIngiLCAxOjMpYCBtYXRjaGVzIHgxLCB4MiBhbmQgeDMgIAoKIyMgVmFyaWFudHMgb24gYHNlbGVjdCgpYAoKKiBgcmVuYW1lKClgIGlzIGNvbnNpZGVyZWQgYSB2YXJpYW50IG9mIHNlbGVjdCgpIHdoZXJlIHlvdSB0YWtlIGEgY29sdW1uLCBjaGFuZ2UgaXRzIG5hbWUsIGFuZCBrZWVwIGFsbCBvdGhlciBjb2x1bW5zIGFzIHdlbGwuCiogYGV2ZXJ5dGhpbmcoKWAgaXMgYSBoZWxwZXIgZm9yIHNlbGVjdCgpIHRoYXQgbGV0cyB5b3UgbW92ZSBvbmUgb3IgYSBmZXcgY29sdW1ucyB0byB0aGUgYmVnaW5uaW5nIChsZWZ0KSBvZiB0aGUgdGFibGUsIHdoaWxlIHJldGFpbmluZyBhbGwgb3RoZXIgY29sdW1ucy4gIAoKIyBgbXV0YXRlKClgIGlzIGZvciBjcmVhdGluZyBuZXcgdmFyaWFibGVzIGZyb20gb2xkICAKCiogVGhlIG5ldyBjb2x1bW5zIGFyZSBhZGRlZCBhdCB0aGUgZW5kIChyaWdodCkgb2YgdGhlIGRhdGEgZnJhbWUuICAKKiBZb3UgY2FuIGFkZCBtdWx0aXBsZSBjb2x1bW5zIGluIG9uZSBjYWxsLCBhbmQgdGhlIGxhdGVyIGNvbHVtbnMgY2FuIHVzZSB2YXJpYWJsZXMgZGVmaW5lZCBlYXJsaWVyIGluIHRoZSBjYWxsICAKKiBgdHJhbnNtdXRlKClgIGlzIGEgdmFyaWFudCB3aGVyZSB5b3Uga2VlcCBPTkxZIHRoZSBuZXdseSBkZWZpbmVkIGNvbHVtbnMgIAoKYGBge3IgbXV0YXRlIGV4YW1wbGVzLCBlY2hvID0gVFJVRX0KKGZsaWdodHNfc21sIDwtIHNlbGVjdChmbGlnaHRzLCAKICB5ZWFyOmRheSwgCiAgZW5kc193aXRoKCJkZWxheSIpLCAKICBkaXN0YW5jZSwgCiAgYWlyX3RpbWUKKSkKKGZsaWdodHNfbGVzc19zbWwgPC0gbXV0YXRlKGZsaWdodHNfc21sLAogIGdhaW4gPSBkZXBfZGVsYXkgLSBhcnJfZGVsYXksCiAgc3BlZWQgPSBkaXN0YW5jZSAvIGFpcl90aW1lICogNjAKKSkKKGZsaWdodHNfZXZlbl9tb3JlIDwtIG11dGF0ZShmbGlnaHRzX3NtbCwKICBnYWluID0gZGVwX2RlbGF5IC0gYXJyX2RlbGF5LAogIGhvdXJzID0gYWlyX3RpbWUgLyA2MCwKICBnYWluX3Blcl9ob3VyID0gZ2FpbiAvIGhvdXJzCikpCihmbGlnaHRzX25ldyA8LSB0cmFuc211dGUoZmxpZ2h0cywKICBnYWluID0gZGVwX2RlbGF5IC0gYXJyX2RlbGF5LAogIGhvdXJzID0gYWlyX3RpbWUgLyA2MCwKICBnYWluX3Blcl9ob3VyID0gZ2FpbiAvIGhvdXJzCikpCmBgYAoKIyMgTWFueSB2ZWN0b3JpemVkIGZ1bmN0aW9ucyB3b3JrIHdpdGggYG11dGF0ZSgpYCAgCgpTb21lIGV4YW1wbGVzOiAgCiogQXJpdGhtZXRpYywgbG9ncyAodmVjdG9ycyByZWN5Y2xlIHRvIG1hdGNoIGxlbmd0aCkgIAoqIE1vZHVsdXMgYXJpdGhtZXRpYzogICUvJSAoaW50ZWdlciBkaXZpc2lvbikgYW5kICUlICAgKHJlbWFpbmRlciksIHdoZXJlIHggPT0geSAqICh4ICUvJSB5KSArICh4ICUlIHkpICAKKiBTdXJyb3VuZGluZyB2YWx1ZXM6IGxlYWQoKSwgbGFnKCkgIAoqIEN1bXVsYXRpdmUgdmFsdWVzIHN1Y2ggYXMgY3Vtc3VtKCkgYW5kIGN1bXByb2QoKSAgCiogV2l0aCB0aGUgUmNwcHJvbGwgcGFja2FnZSwgcm9sbGluZyBzdW1zLCBldGMuICAKKiBSYW5raW5nIHN1Y2ggYXMgbWluX3JhbmsoKSAgCgojIyBFeGVyY2lzZXMgZm9yIGBtdXRhdGUoKWAgIAoKMS4gQ3VycmVudGx5IGRlcF90aW1lIGFuZCBzY2hlZF9kZXBfdGltZSBhcmUgY29udmVuaWVudCB0byBsb29rIGF0LCBidXQgaGFyZCB0byBjb21wdXRlIHdpdGggYmVjYXVzZSB0aGV54oCZcmUgbm90IHJlYWxseSBjb250aW51b3VzIG51bWJlcnMuIENvbnZlcnQgdGhlbSB0byBhIG1vcmUgY29udmVuaWVudCByZXByZXNlbnRhdGlvbiBvZiBudW1iZXIgb2YgbWludXRlcyBzaW5jZSBtaWRuaWdodC4gIAoKMi4gQ29tcGFyZSBhaXJfdGltZSB3aXRoIGFycl90aW1lIC0gZGVwX3RpbWUuIFdoYXQgZG8geW91IGV4cGVjdCB0byBzZWU/IFdoYXQgZG8geW91IHNlZT8gV2hhdCBkbyB5b3UgbmVlZCB0byBkbyB0byBmaXggaXQ/ICAKCipUaGUgdGltZXMgYXJlIGluIEhITU0gZm9ybWF0LCBzbyBzdHJhaWdodCBzdWJ0cmFjdGlvbiBkb2VzIG5vdCB3b3JrLiBXZSBjYW4gY29udmVydCB0byB0aW1lIG9iamVjdHMsIG9yIHVzZSBtb2R1bHVzIGFyaXRobWV0aWMgdG8gY29udmVydCB0byBtaW51dGVzIGFuZCBiYWNrIHRvIHRpbWUuICAKCjMuIENvbXBhcmUgZGVwX3RpbWUsIHNjaGVkX2RlcF90aW1lLCBhbmQgZGVwX2RlbGF5LiBIb3cgd291bGQgeW91IGV4cGVjdCB0aG9zZSB0aHJlZSBudW1iZXJzIHRvIGJlIHJlbGF0ZWQ/ICAKCipXZSBleHBlY3QgZGVwX2RlbGF5ID0gc2NoZWRfZGVwX3RpbWUgLSBkZXBfdGltZS4gSG93ZXZlciwgZm9yID4gNjAgbWluIGRpZmZlcmVuY2UsIHdlIGhhdmUgdGhlIHNhbWUgdGltZSBpc3N1ZSwgYW5kIHNvbHV0aW9uLCBhcyBpbiBleGVyY2lzZSAyLioKCjQuIEZpbmQgdGhlIDEwIG1vc3QgZGVsYXllZCBmbGlnaHRzIHVzaW5nIGEgcmFua2luZyBmdW5jdGlvbi4gSG93IGRvIHlvdSB3YW50IHRvIGhhbmRsZSB0aWVzPyBDYXJlZnVsbHkgcmVhZCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgbWluX3JhbmsoKS4gIAoKKiAqYHJhbmsoKWAgZ29lcyBzbWFsbGVzdCB0byBsYXJnZXN0LCBnaXZlcyBkZWNpbWFscyAoYXZlcmFnZXMpIGZvciB0aWVzLCAxLCAyLjUsIDIuNSwgNCwuLi4gYW5kIHB1dHMgTkFzIGxhc3QgYnkgZGVmYXVsdCwgYnV0IGNhbiBjaGFuZ2UgYmVoYXZpb3IgdXNpbmcgYG5hLmxhc3RgLiAgYHJhbmsoZGVzYygpKWAgZ29lcyBsYXJnZXN0IHRvIHNtYWxsZXN0LiogIAoqICpgcmFuaygpYCBoYXMgZGlmZmVyZW50IG1ldGhvZHMgb2YgaGFuZGxpbmcgdGllcywgcHJvZHVjaW5nIGFsbC1pbnRlZ2VyIG91dHB1dC4gRmlyc3Q7IGxhc3Q7IHJhbmRvbTsgbWF4IChtYXggcmFuayBvZiBhbGwgdGllcyBzbyB0aGVyZSBtaWdodCBub3QgYmUgYSAjMSk7IG1pbiAobWluIHJhbmsgb2YgYWxsIHRpZWQgZWxlbWVudHMgImFzIHVzZWQgaW4gc3BvcnRzLCIgc28geW91IGNhbiAidGllIGZvciAxc3QiIC0tICgxLDEsMyw0Li4uKSkgLS0gdGhpcyBpcyBtaW5fcmFuaygpLiBgZmlyc3RgLCBgbGFzdGAsIGByYW5kb21gIHByb2R1Y2UgYWxsLXVuaXF1ZSByYW5rcy4qICAKKiAqSWYgeW91IHByZXNlcnZlIG9yIGF2ZXJhZ2UgdGllcywgeW91IGNvdWxkIGVuZCB1cCB3aXRoIG1vcmUgb3IgZmV3ZXIgdGhhbiAxMCBpbiB0aGUgcmVzdWx0LiBUaGVyZWZvcmUgdG8gZ2V0IGV4YWN0bHkgMTAgd2Ugd2lsbCBicmVhayB0aWVzIHJhbmRvbWx5LiogCiogKklmIHlvdSB3YW50IHRvIHVzZSBhbm90aGVyIGNvbHVtbiB0byBicmVhayB0aWVzLCB0aGVuIEkgZG9uJ3Qgc2VlIGEgd2F5IHdpdGhpbiBgcmFuaygpYCBmdW5jdGlvbnMuIFlvdSBjb3VsZCB1c2UgYGFycmFuZ2UoKWAgYW5kIHRoZW4gcm93IG51bWJlcnMuKiAgCgo1LiBXaGF0IGRvZXMgMTozICsgMToxMCByZXR1cm4/IFdoeT8gIAoKKlRoZSAxOjMgdmVjdG9yIGlzIHJlY3ljbGVkLCBzbyB5b3UgZ2V0IDQrMSwgNSsyLCBldGMuKiAgCgo2LiBXaGF0IHRyaWdvbm9tZXRyaWMgZnVuY3Rpb25zIGRvZXMgUiBwcm92aWRlPyAgCgoqVGhlIHVzdWFsIGZ1bmN0aW9ucywgcGx1cyBhIGZldyBvdGhlcnMuIEFuZ2xlcyBhcmUgaW4gcmFkaWFucy4gYGNvc3BpKHgpYCBhbmQga2luIGdpdmUgdGhlIGZ1bmN0aW9ucyBvZiBwaSB0aW1lcyB4LCBvbmx5IGZvciB4ID0gbXVsdGlwbGVzIG9mIDAuNSoKCmBgYHtyIG11dGF0ZSBleGVyY2lzZXMsIGVjaG8gPVRSVUV9CiMgVGhlIHRpbWUgaXMgaW4gSE1NIG9yIEhITU0gZm9ybWF0LiBUaGVyZWZvcmUgdG8gZ2V0IHRoZSBob3VycyB3ZSBkaXZpZGUgYnkgMTAwLCBhbmQgZm9yIG1pbnV0ZXMgd2UgdGFrZSB0aGUgcmVtYWluZGVyLiAoRGlkIG5vdCBkZWFsIHdpdGggdGhlIHRpbWVzID0gMjQwMCEpCgooYmV0dGVyX3RpbWUgPC0gbXV0YXRlKGZsaWdodHMsIGRlcF9taW5fc2luY2VfbWlkbmlnaHQgPSAoZGVwX3RpbWUgJS8lIDEwMCkgKiA2MCArIGRlcF90aW1lICUlIDEwMCwgc2NoZWRfZGVwX21pbl9zaW5jZV9taWRuaWdodCA9IChzY2hlZF9kZXBfdGltZSAlLyUgMTAwKSAqIDYwICsgc2NoZWRfZGVwX3RpbWUgJSUgMTAwKSkKCihjb21wYXJlIDwtIHNlbGVjdChmbGlnaHRzLCBhaXJfdGltZSwgYXJyX3RpbWUsIGRlcF90aW1lKSAlPiUgbXV0YXRlKHNwZW50X3RpbWUgPSBhcnJfdGltZS1kZXBfdGltZSkpCgojIHRoZSB0aW1lcyBhcmUgaW4gSEhNTSBmb3JtYXQsIHNvIHN0cmFpZ2h0IHN1YnRyYWN0aW9uIGRvZXMgbm90IHdvcmsKCihyZWFsdGltZXMgPC0gbXV0YXRlKGZsaWdodHMsIAogICAgICAgICAgICAgICAgICAgIGFycl90aW1lX2htcyA9IGhtcyhOVUxMLCBhcnJfdGltZSAlJSAxMDAsIGFycl90aW1lICUvJSAxMDApLAogICAgICAgICAgICAgICAgICAgIGRlcF90aW1lX2htcyA9IGhtcyhOVUxMLCBkZXBfdGltZSAlJSAxMDAsIGRlcF90aW1lICUvJSAxMDApLAogICAgICAgICAgICAgICAgICAgIHNwZW50X3RpbWVfaG1zID0gZGlmZnRpbWUoYXJyX3RpbWVfaG1zLCBkZXBfdGltZV9obXMsIHVuaXRzID0gIm1pbnMiKSkpCiAgCiMgbXV0YXRlIHRvIGFkZCB0aGUgcmFua2luZyBmdW5jdGlvbiAoYWx0ZXJuYXRpdmVseSB5b3UgY291bGQgcHJvYmFibHkgYXJyYW5nZSBhbmQgdGhlbiB0YWtlIGJ5IHJvdyBudW1iZXIpCgojIGlmIHlvdSBwcmVzZXJ2ZSBvciBhdmVyYWdlIHRpZXMsIHlvdSBjb3VsZCBlbmQgdXAgd2l0aCBtb3JlIG9yIGZld2VyIHRoYW4gMTAgaW4gdGhlIHJlc3VsdC4gVGhlcmVmb3JlIHRvIGdldCBleGFjdGx5IDEwIHdlIHdpbGwgYnJlYWsgdGllcyByYW5kb21seS4KCihhcnJfcmFua2VkIDwtIG11dGF0ZShmbGlnaHRzLCBhcnJfZGVsYXlfcmFuayA9IHJhbmsoZGVzYyhhcnJfZGVsYXkpLCB0aWVzLm1ldGhvZCA9ICJyYW5kb20iKSkpCih0b3BfMTBfZGVsYXkgPC0gYXJyX3JhbmtlZCAlPiUgIGZpbHRlcihhcnJfZGVsYXlfcmFuayA8PSAxMCkpCmBgYAoKIyBgc3VtbWFyaXNlKClgIGNvbGxhcHNlcyBncm91cHMgaW50byBzaW5nbGUgdmFsdWVzICAKCipXaGVuIHVzaW5nIGBzdW1tYXJpc2UoKWAgeW91IHVzdWFsbHkgYWxzbyB3YW50IGdyb3VwcyBjcmVhdGVkIGJ5IGBncm91cF9ieSgpYC4gKElmIHlvdSBkaWRuJ3QgaGF2ZSBhIGdyb3VwLCB5b3UgY291bGQganVzdCBjYWxsIHRoZSBkZXNpcmVkIGZ1bmN0aW9ucyBvbiB3aG9sZSBjb2x1bW5zLikgIAoKKlVzaW5nIHRoZSBwaXBlIHdlIGNhbiBxdWlja2x5IGZlZWQgZ3JvdXBzIGludG8gc3VtbWFyaWVzIGFuZCBkbyBvdGhlciB1c2VmdWwgdGFza3MgZWZmaWNpZW50bHkuICAKCipBbW9uZyB0aWR5dmVyc2UgZnVuY3Rpb25zLCBvbmx5IGdncGxvdDIgZG9lc24ndCB3b3JrIGFzIHdlbGwgd2l0aCBwaXBlczogIml0IHdhcyB3cml0dGVuIGJlZm9yZSB0aGUgcGlwZSB3YXMgZGlzY292ZXJlZC4gVW5mb3J0dW5hdGVseSwgdGhlIG5leHQgaXRlcmF0aW9uIG9mIGdncGxvdDIsIGdndmlzLCB3aGljaCBkb2VzIHVzZSB0aGUgcGlwZSwgaXNu4oCZdCBxdWl0ZSByZWFkeSBmb3IgcHJpbWUgdGltZSB5ZXQuIiAgQnV0IGl0IGlzIHVzZWQgaW4gc29tZSBleGFtcGxlcyBiZWxvdy4gIAoKKkJld2FyZSAtLSBpZiB5b3UgaGF2ZSBhbnkgbWlzc2luZyB2YWx1ZXMgKE5BcykgaW4geW91ciBkYXRhIHlvdXIgc3VtbWFyeSB2YWx1ZSB3aWxsIGFsc28gYmUgTkEsIHVubGVzcyB5b3Ugc2V0IGBuYS5ybSA9IFRSVUVgLiAgCgoqICIuLi5pdOKAmXMgYWx3YXlzIGEgZ29vZCBpZGVhIHRvIGluY2x1ZGUgZWl0aGVyIGEgY291bnQgKG4oKSksIG9yIGEgY291bnQgb2Ygbm9uLW1pc3NpbmcgdmFsdWVzIChzdW0oIWlzLm5hKHgpKSkuIFRoYXQgd2F5IHlvdSBjYW4gY2hlY2sgdGhhdCB5b3XigJlyZSBub3QgZHJhd2luZyBjb25jbHVzaW9ucyBiYXNlZCBvbiB2ZXJ5IHNtYWxsIGFtb3VudHMgb2YgZGF0YS4iICAKKiogaW4gdGhlIGV4YW1wbGUsIG5vdGUgdGhhdCAid2hlbmV2ZXIgeW91IHBsb3QgYSBtZWFuIChvciBvdGhlciBzdW1tYXJ5KSB2cy4gZ3JvdXAgc2l6ZSwgeW914oCZbGwgc2VlIHRoYXQgdGhlIHZhcmlhdGlvbiBkZWNyZWFzZXMgYXMgdGhlIHNhbXBsZSBzaXplIGluY3JlYXNlcy4iICAKCiMjIE90aGVyIGZ1bmN0aW9ucyBmb3Igc3VtbWFyaXNpbmcgW3NpY10gIAoKKiBDb3VudCwgU3VtLCBNZWRpYW4sIGV0Yy4gIAoqIE1lYXN1cmVzIG9mIHNwcmVhZDogc2QoeCksIElRUih4KSwgbWFkKHgpLiBUaGUgaW50ZXJxdWFydGlsZSByYW5nZSBJUVIoeCkgYW5kIG1lZGlhbiBhYnNvbHV0ZSBkZXZpYXRpb24gbWFkKHgpIGFyZSByb2J1c3QgZXF1aXZhbGVudHMgdGhhdCBtYXkgYmUgbW9yZSB1c2VmdWwgaWYgeW91IGhhdmUgb3V0bGllcnMuICAKKiBTdW1tYXJpZXMgb2YgbG9naWNhbCBzdWJzZXRzIHN1Y2ggYXMgYG1lYW4oYXJyX2RlbGF5W2Fycl9kZWxheSA+IDBdKWAgPSB0aGUgbWVhbiBvZiBhbGwgdGhvc2UgdmFsdWVzID4gMCAgCiogUmFua2luZ3M6IG1pbih4KSwgcXVhbnRpbGUoeCwgMC4yNSkgWzI1dGggcGVyY2VudGlsZSB2YWx1ZV0sIG1heCh4KQoqIFBvc2l0aW9uczogZmlyc3QoeCksIG50aCh4LCAyKSwgbGFzdCh4KS4gVGhlc2Ugd29yayBzaW1pbGFybHkgdG8geFsxXSwgeFsyXSwgYW5kIHhbbGVuZ3RoKHgpXSBidXQgbGV0IHlvdSBzZXQgYSBkZWZhdWx0IHZhbHVlIGlmIHRoYXQgcG9zaXRpb24gZG9lcyBub3QgZXhpc3QgCgogIApgYGB7ciBncm91cCBzdW1tYXJpc2UgYW5kIHBpcGUgZXhhbXBsZXMsIGVjaG8gPSBUUlVFfQoKIyB3aXRoIHBpcGUKKGRlbGF5cyA8LSBmbGlnaHRzICU+JSAKICBncm91cF9ieShkZXN0KSAlPiUgCiAgc3VtbWFyaXNlKAogICAgY291bnQgPSBuKCksCiAgICBkaXN0ID0gbWVhbihkaXN0YW5jZSwgbmEucm0gPSBUUlVFKSwKICAgIGRlbGF5ID0gbWVhbihhcnJfZGVsYXksIG5hLnJtID0gVFJVRSkKICApICU+JSAKICBmaWx0ZXIoY291bnQgPiAyMCwgZGVzdCAhPSAiSE5MIikpCiMgcGxvdCBkZWxheSB2cyBkaXN0YW5jZQoocCA8LSBnZ3Bsb3QoZGF0YSA9IGRlbGF5cywgbWFwcGluZyA9IGFlcyh4ID0gZGlzdCwgeSA9IGRlbGF5KSkgKwogICBnZW9tX3BvaW50KGFlcyhzaXplID0gY291bnQpLCBhbHBoYSA9IDEvMykgKwogICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKQopCiMgcmVtb3ZlIE5BcyB3aGVuIGNvbXB1dGluZyB0aGUgc3VtbWFyeQooZGVsYXllZCA8LSBmbGlnaHRzICU+JSAKICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5KSAlPiUgCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkpCgojIHJlbW92ZSBOQXMgYnkgcmVtb3ZpbmcgZGF0YSBhdCB0aGUgYmVnaW5uaW5nCihub3RfY2FuY2VsbGVkIDwtIGZsaWdodHMgJT4lIAogIGZpbHRlcighaXMubmEoZGVwX2RlbGF5KSwgIWlzLm5hKGFycl9kZWxheSkpCikKKHRydWVfZGVsYXllZCA8LSBub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5KSAlPiUgCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkpCgojIHdoaWNoIHBsYW5lcyBoYXZlIHRoZSBncmVhdGVzdCBkZWxheXM/CihkZWxheXMgPC0gbm90X2NhbmNlbGxlZCAlPiUgCiAgZ3JvdXBfYnkodGFpbG51bSkgJT4lIAogIHN1bW1hcmlzZSgKICAgIGRlbGF5ID0gbWVhbihhcnJfZGVsYXkpCiAgKSkKIyBoaXN0b2dyYW0gb2YgYXZlcmFnZSBkZWxheSB0aW1lcwoocDEgPC0gZ2dwbG90KGRhdGEgPSBkZWxheXMsIG1hcHBpbmcgPSBhZXMoeCA9IGRlbGF5KSkgKyAKICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMTApKQoKIyByZW1vdmUgTkFzIGFuZCBjYW5jZWxsZWQKKGRlbGF5cyA8LSBub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieSh0YWlsbnVtKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSwKICAgIG4gPSBuKCkKICApKQojIHNjYXR0ZXJwbG90IG9mIGF2ZXJhZ2UgZGVsYXkgdnMgbiAocm90YXRlZCBoaXN0b2dyYW0gYWN0dWFsbHksIGJ1dCB5b3UgY2FuIHNlZSBtb3JlIHBvaW50cykKIyBzaG93cyB0aGF0IHRoZSBoaWdoIGF2ZXJhZ2VzIGFyZSBhbG1vc3QgYWxsIGJhc2VkIG9uIGZldyBkYXRhIHBvaW50cwoocDMgPC0gZ2dwbG90KGRhdGEgPSBkZWxheXMsIG1hcHBpbmcgPSBhZXMoeCA9IG4sIHkgPSBkZWxheSkpICsgCiAgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApKQoKIyBmaWx0ZXIgb3V0IHRoZSBwb2ludHMgd2l0aCBsb3cgbiBiZWZvcmUgcGxvdHRpbmcKKHA0IDwtIGRlbGF5cyAlPiUgCiAgZmlsdGVyKG4gPiAyNSkgJT4lIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBuLCB5ID0gZGVsYXkpKSArIAogICAgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApKQoKIyBpbnN0ZWFkIG9mIGF2ZXJhZ2luZyBlYXJseSBhbmQgbGF0ZSBmbGlnaHRzIHRvZ2V0aGVyLCBzdGFydCB3aXRoIHRoZSBsb2dpY2FsIHN1YnNldCBvZiB0aGUgcG9zaXRpdmUgKGxhdGUpIGRlbGF5cwoobm90X2NhbmNlbGxlZCAlPiUgCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheSkgJT4lIAogIHN1bW1hcmlzZSgKICAgIGF2Z19kZWxheTEgPSBtZWFuKGFycl9kZWxheSksCiAgICBhdmdfZGVsYXkyID0gbWVhbihhcnJfZGVsYXlbYXJyX2RlbGF5ID4gMF0pICMgdGhlIGF2ZXJhZ2UgcG9zaXRpdmUgZGVsYXkKICApKQoKIyBsb29raW5nIGF0IHN0YW5kYXJkIGRldmlhdGlvbiBvZiBkaXN0YW5jZSB0byBlYWNoIGRlc3RpbmF0aW9uCihzcHJlYWRfb2Zfb3JpZ2lucyA8LSBub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieShkZXN0KSAlPiUgCiAgc3VtbWFyaXNlKGRpc3RhbmNlX3NkID0gc2QoZGlzdGFuY2UpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGRpc3RhbmNlX3NkKSkpCgojIFdoZW4gZG8gdGhlIGZpcnN0IGFuZCBsYXN0IGZsaWdodHMgbGVhdmUgZWFjaCBkYXk/CihmaXJzdF9hbmRfbGFzdCA8LSBub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5KSAlPiUgCiAgc3VtbWFyaXNlKAogICAgZmlyc3QgPSBtaW4oZGVwX3RpbWUpLAogICAgbGFzdCA9IG1heChkZXBfdGltZSkKICApKQoKKGZpcnN0X2xhc3RfMiA8LSBub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5KSAlPiUgCiAgc3VtbWFyaXNlKAogICAgZmlyc3RfZGVwID0gZmlyc3QoZGVwX3RpbWUpLCAjIFRoaXMgd29ya3MgT05MWSBiZWNhdXNlIHRoZSB0YWJsZSBpcyBhbHJlYWR5IG9yZGVyZWQgYnkgZGVwX3RpbWUhCiAgICBsYXN0X2RlcCA9IGxhc3QoZGVwX3RpbWUpCiAgKSkKCiMgYW5vdGhlciB3YXkgdG8gZmluZCB0aGUgMXN0IGFuZCBsYXN0IHBlciBkYXkKKHJhbmtmaWx0IDwtIG5vdF9jYW5jZWxsZWQgJT4lIAogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXkpICU+JSAKICBtdXRhdGUociA9IG1pbl9yYW5rKGRlc2MoZGVwX3RpbWUpKSkgJT4lIAogIGZpbHRlcihyICVpbiUgcmFuZ2UocikpKSAjIHJhbmdlIGdpdmVzIHlvdSB0aGUgbWluIGFuZCBtYXggdmFsdWVzCmBgYAoKCgpIZXJlIHdlIGNvbnRpbnVlIGRpZ3Jlc3NpbmcgKElNTykgb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhdGlvbiBhbmQgc2FtcGxlIHNpemUsIHVzaW5nIGV4YW1wbGVzIGZyb20gdGhlIExhaG1hbiBiYXNlYmFsbCBzdGF0aXN0aWNzIHBhY2thZ2UuCgpgYGB7ciBhZ2dyZWdhdGUgdmFyaWF0aW9uIGV4YW1wbGVzLCBlY2hvID0gVFJVRX0KIyBDb252ZXJ0IHRvIGEgdGliYmxlIHNvIGl0IHByaW50cyBuaWNlbHkKKGJhdHRpbmcgPC0gYXNfdGliYmxlKExhaG1hbjo6QmF0dGluZykpCgojIGJhdHRpbmcgYXZlcmFnZSB2cyBhdCBiYXRzIHBlciBwbGF5ZXIKCiMgd2hvIGFyZSB0aGUgImJlc3QiIGJhdHRlcnM/IFRoZSBsdWNreSBvbmVzPyBOb3RlIHRoZSBhdC1iYXRzIGZvciB0aGUgdG9wIG9mIHRoZSBsaXN0CihoaWdoX2F2ZyA8LSBiYXR0ZXJzICU+JSAKICBhcnJhbmdlKGRlc2MoYmEpKQopCiMgdmlzdWFsaXplIGF2ZyB3aXRoIHRoZSBudW1iZXIgb2YgYXQtYmF0cyAKKGJhdHRlcnMgPC0gYmF0dGluZyAlPiUgCiAgZ3JvdXBfYnkocGxheWVySUQpICU+JSAKICBzdW1tYXJpc2UoCiAgICBiYSA9IHN1bShILCBuYS5ybSA9IFRSVUUpIC8gc3VtKEFCLCBuYS5ybSA9IFRSVUUpLAogICAgYWIgPSBzdW0oQUIsIG5hLnJtID0gVFJVRSkKICApKQoKKHAgPC0gYmF0dGVycyAlPiUgCiAgZmlsdGVyKGFiID4gMTAwKSAlPiUgCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGFiLCB5ID0gYmEpKSArCiAgICBnZW9tX3BvaW50KCkgKyAKICAgIGdlb21fc21vb3RoKHNlID0gRkFMU0UpKQpgYGAKCmBgYHtyIGNvdW50IGV4YW1wbGVzLCBlY2hvID0gVFJVRX0KIyBXaGljaCBkZXN0aW5hdGlvbnMgaGF2ZSB0aGUgbW9zdCBjYXJyaWVycz8KKG5vdF9jYW5jZWxsZWQgJT4lIAogIGdyb3VwX2J5KGRlc3QpICU+JSAKICBzdW1tYXJpc2UoY2FycmllcnMgPSBuX2Rpc3RpbmN0KGNhcnJpZXIpKSAlPiUgIyB1bmlxdWUgdmFsdWVzCiAgYXJyYW5nZShkZXNjKGNhcnJpZXJzKSkpCgojIGhvdyBtYW55IHZhbHVlcyBhcmUgZW50ZXJlZD8KKHZhbGlkX2Fycl90aW1lIDwtIHN1bSghaXMubmEobm90X2NhbmNlbGxlZCRhcnJfdGltZSkpCiMgb3IgdG8gZ2V0IHRoZSBmcmFjdGlvbiBvZiB2YWxpZCB2YWx1ZXMKKHZhbGlkX2Fycl90aW1lX2ZyYWMgPC0gbWVhbighaXMubmEobm90X2NhbmNlbGxlZCRhcnJfdGltZSkpKQoodmFsaWRfYXJyX3RpbWVfZnJhYzIgPC0gbWVhbighaXMubmEoZmxpZ2h0cyRhcnJfdGltZSkpKQoKIyBjb3VudCgpIGlzIGEgZHBseXIgc3VtbWFyeSBvZiBhIGdyb3VwZWQgdGFibGUKCihmbGlnaHRzX3Blcl9kZXN0IDwtIG5vdF9jYW5jZWxsZWQgJT4lIAogIGNvdW50KGRlc3QpKQoKIyB5b3UgY2FuIHdlaWdodCBjb3VudHMgYnkgYW5vdGhlciB2YXJpYWJsZQoKKGFpcmNyYWZ0X21pbGVzIDwtIG5vdF9jYW5jZWxsZWQgJT4lIAogIGNvdW50KHRhaWxudW0sIHd0ID0gZGlzdGFuY2UpKQoKCmBgYAoKCgoKCg==